Scenario #9690: A Global Admin Can Create a Read Only Api Key Subject

The endpoint-scope *:read makes an API-key read-only: it allows all GET endpoints under /api/, but nothing which changes data. With the global ADMIN role granted to its API_KEY subject, such an API-key can read everything, e.g. for reporting or monitoring, without any risk of modifying data.

Properties

Given

name value
subjectUuid a91c0009-0000-0000-0000-000000000009
subjectName readonly.key

Create the read-only API_KEY Subject

HTTP POST "/api/rbac/subjects" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  <<EOF
{
  "uuid" : "a91c0009-0000-0000-0000-000000000009",
  "name" : "readonly.key",
  "type" : "API_KEY",
  "scopes" : [ "*:read" ]
}
EOF
=> status: 201 CREATED a91c0009-0000-0000-0000-000000000009
{
  "uuid" : "a91c0009-0000-0000-0000-000000000009",
  "name" : "readonly.key",
  "organization" : "readonly",
  "type" : "API_KEY",
  "apiKey" : "hsak_readonly.key.8381dc9dbc87d8a70087d8ed66deab11b7cd7536dbb14ca931dba8c9120bd88c",
  "scopes" : [ "*:read" ],
  "expiresAt" : null
}

Prerequisite: Resolve the UUID of the global ADMIN role

The grant API needs the UUID of the role which we want to grant.

HTTP GET "/api/rbac/roles?name=rbac.global%23global%3AADMIN" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }`
=> status: 200 OK 
[ {
  "uuid" : "16e992d4-bed6-4678-9c76-8ee498fb13db", // globalAdminRoleUuidToGrant
  "object.uuid" : "d906a1a8-bf20-4c1e-8251-8101f1d69af2",
  "objectTable" : "rbac.global",
  "objectIdName" : "global",
  "roleType" : "ADMIN",
  "roleName" : "rbac.global#d906a1a8-bf20-4c1e-8251-8101f1d69af2:ADMIN",
  "roleIdName" : "rbac.global#global:ADMIN"
} ]

Grant the global ADMIN role to the API_KEY Subject

HTTP POST "/api/rbac/grants" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  -H 'Hostsharing-Assumed-Roles: rbac.global#global:ADMIN' \
  <<EOF
{
  "assumed" : true,
  "grantedRole.uuid" : "16e992d4-bed6-4678-9c76-8ee498fb13db", // globalAdminRoleUuidToGrant
  "granteeSubject.uuid" : "a91c0009-0000-0000-0000-000000000009"
}
EOF
=> status: 201 CREATED 16e992d4-bed6-4678-9c76-8ee498fb13db // globalAdminRoleUuidToGrant

Verify the read-only API-key can use GET endpoints, e.g. list all memberships

HTTP GET "/api/hs/office/memberships" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK 
[
  {
    "uuid" : "a05b3e87-6a11-4096-9fba-ae7da4271ee3", // Membership: M-3101000 - Test AG
    "partner" : {
      "uuid" : "215088cb-041a-4cc6-8657-7e949cf8dd5f", // Partner: P-31010 - Test AG
      "partnerNumber" : "P-31010",
      "partnerRel" : {
        "uuid" : "9c94cdf2-a746-4438-af53-664b87483a65", // partnerRelationUuid
        "anchor" : {
          "uuid" : "c43fc218-ae0e-4d6d-afc9-528d9861c2eb",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "efe3662a-02d9-4fa9-a4f0-29dfd893fd78", // Person: Test AG
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Test AG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "7e8be3df-3e34-4548-8b35-d630a30bfd4a", // Contact: Test AG - China
          "caption" : "Test AG - China",
          "postalAddress" : {
            "country" : "China",
            "province" : "Guangdong Province",
            "city" : "Dongguan City",
            "street" : "No.2 Commercial Second Street",
            "district" : "Niushan Wei Wu",
            "department" : "Executive Board",
            "building" : "Thi Chi Koh Building"
          },
          "emailAddresses" : {
            "main" : "norden@test-ag.example.org"
          },
          "phoneNumbers" : {
            "phone" : "++15 999 654321"
          }
        }
      },
      "details" : {
        "uuid" : "06e11b1f-3c8c-464e-83e8-a9f761b853d3",
        "registrationOffice" : "Registergericht Hamburg",
        "registrationNumber" : "1234567",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-3101000",
    "memberNumberSuffix" : "00",
    "validFrom" : "2020-10-15",
    "validTo" : "2023-12-31",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  {
    "uuid" : "386a7905-0627-493c-b271-e524f77515aa",
    "partner" : {
      "uuid" : "76e93f3d-b4d9-437f-8305-703ec7ee0892",
      "partnerNumber" : "P-10001",
      "partnerRel" : {
        "uuid" : "bb538c43-04f6-4562-a794-929c3f4e7102",
        "anchor" : {
          "uuid" : "c43fc218-ae0e-4d6d-afc9-528d9861c2eb",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "20843a67-f6d5-415a-af9d-71319bb7df43", // Person: First GmbH
          "personType" : "LEGAL_PERSON",
          "tradeName" : "First GmbH",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "162c1c24-508f-4bf2-a4ce-9b11b36e39d8", // Contact: contact-admin@firstcontact.example.com
          "caption" : "first contact",
          "postalAddress" : {
            "country" : "Germany"
          },
          "emailAddresses" : {
            "main" : "contact-admin@firstcontact.example.com"
          },
          "phoneNumbers" : {
            "phone_office" : "+49 123 1234567"
          }
        }
      },
      "details" : {
        "uuid" : "0a575e34-96ac-4702-b898-6d3644b91a4e",
        "registrationOffice" : "Hamburg",
        "registrationNumber" : "RegNo123456789",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-1000101",
    "memberNumberSuffix" : "01",
    "validFrom" : "2022-10-01",
    "validTo" : "2024-12-30",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  {
    "uuid" : "bdcd792f-bd44-4a30-80dd-3b9adb5b0edd",
    "partner" : {
      "uuid" : "b331c6cf-b342-4889-a41c-625cb730445b", // partnerUuid
      "partnerNumber" : "P-10002",
      "partnerRel" : {
        "uuid" : "12f3a7bd-5bc4-489b-8b8d-d064d93922a5",
        "anchor" : {
          "uuid" : "c43fc218-ae0e-4d6d-afc9-528d9861c2eb",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "ce7a2acb-db99-43f9-8a7e-0129a39c5138",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.",
          "salutation" : null,
          "title" : null,
          "givenName" : "Peter",
          "familyName" : "Smith"
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "0124ef1a-74af-499b-a0f0-660c89687738",
          "caption" : "second contact",
          "postalAddress" : {
            "country" : "Germany"
          },
          "emailAddresses" : {
            "main" : "contact-admin@secondcontact.example.com"
          },
          "phoneNumbers" : {
            "phone_office" : "+49 123 1234567"
          }
        }
      },
      "details" : {
        "uuid" : "a6f235a1-36c0-4e92-8a9e-5525f45c8afc",
        "registrationOffice" : "Hamburg",
        "registrationNumber" : "RegNo123456789",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-1000202",
    "memberNumberSuffix" : "02",
    "validFrom" : "2022-10-01",
    "validTo" : "2025-12-31",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  "..."
]

Verify the read-only API-key cannot write, e.g. not upsert a subject

HTTP PUT "/api/rbac/subjects/a91c0009-0000-0000-0000-000000000009" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY" \
  <<EOF
{
  "name" : "hsh-never_updated",
  "type" : "USER"
}
EOF
=> status: 403 FORBIDDEN 
{
  "path" : "/api/rbac/subjects/a91c0009-0000-0000-0000-000000000009",
  "statusCode" : 403,
  "statusPhrase" : "Forbidden",
  "message" : "ERROR: [403] API-key scopes do not allow PUT /api/rbac/subjects/a91c0009-0000-0000-0000-000000000009"
}

generated on 2026-08-10 04:42:40 for branch HEAD